<?php
/**
 * 从 redis server 中清除已经过期的比赛
 * 派生类需要重载两个函数：[[getObjectFromKey()]] 和 [[canCleanObject()]]，
 * 调用时需要通过 [[clean($config)]] 的 $config 参数传入 redis 模糊键名，如 : ga:kmatch:*
 */
namespace bytefly\yii2common;

use Exception;
use Carbon\Carbon;
use yii\base\BaseObject;
use yii\redis\Connection;

class RedisCleaner extends BaseObject{

    const NotExist = 1;
    const Expired  = 2;
    const ObjectOK = 3;     // 正常状态，不需要删除

    /** 比赛数据在 redis 中保留的天数，超过这个天数的，会被自动删除 */
    public $objectKeepingDays = 7;

    /** @var bool 假装清除标识，设置为 true 时不清除 */
    public $pseudo = false;
    
    /** @var string 泛查找时的键值名字，如 "ga:kmatch:*" */
    public $keys = null;        

    /** @var array 为了支持一个 object 下的多个数据，所以将查询到的 object state 临时缓存起来 */
    public $stateHash = [];
    
    // 检查过程中使用的临时变量
    protected $now;
    public $total = 0;
    public $cleaned = 0;
    public $validKeys = [];

    public static function clean($configs){
        $h = new static($configs);
        $h->execute();
    }

    public function execute(){
        if (empty($this->keys)){
            throw new Exception("缺少 \$keys 参数，请在 [[clean()]] 时传入.");
        }

        $redis = CommonHelper::redis();
        if ($redis == null){
            echo "redis 连接失败\n";
            return;
        }

        $this->now = Carbon::now();

        $keys = $redis->keys($this->keys);
        foreach ($keys as $key) {
            $this->total ++;
            // echo "$key\n";
            $objectId = $this->getObjectIdFromKey($key);
            if ($this->canCleanObject($objectId)){
                $this->delHash($redis, $key);
            }else{
                $this->validKeys[] = $key;
            }
        }

        echo "\n共 {$this->total} 个键，删除了 {$this->cleaned} 个过期的，下面是没有过期的. \n";
        foreach ($this->validKeys as $value) {
            echo "$value\n";
        }

        if($this->pseudo === true){
            echo "由于打开了 pseudo，所以并未执行删除。\n\n";
        }
    }

    protected function delHash(Connection $redis, $key){
        if ($this->pseudo === false){
            $redis->del($key);
        }
        
        $this->cleaned ++;
        echo "del hash: $key\n";
    }

    /** 
     * 检查目标日期是否超时 
     * @param Carbon $time 可以是比赛开始日期，或比赛结束日期
     * @return bool
     */
    protected function isExpired(Carbon $time){
        if ($time->addDays($this->objectKeepingDays)->lt($this->now)){
            return true;
        }

        return false;
    }
    
    protected function getObjectIdFromKey($key) : ?string{
        throw new Exception("派生类需要重载: " .  __FUNCTION__);
    }

    protected function canCleanObject($objectId) : bool{
        throw new Exception("派生类需要重载: " .  __FUNCTION__);
    }
}
?>